﻿/*
 * @author: wizardc
 */

using System;
using System.IO;
using Dou.Utils;
using UnityEditor;
using UnityEngine;

namespace DouEditor.Asset
{
    /// <summary>
    /// 模型文件处理
    /// 
    /// 屏蔽材质导入，如果导入材质，则材质无法进行依赖打包（材质作为子项存在于FBX文件中，而不是以单个文件的形式存在于Assets下）导致AB中
    /// 出现大量的冗余资源，同时材质的Shader是Standard Shader打包耗时很高；模型文件导入后，就是没有材质的网格，需要美术自己新建预制并
    /// 赋予正确的项目中存在的材质；
    /// 
    /// FBX蒙皮动画模型转可使用的预制方法：
    /// 1. 拖入FBX模型，将FBX资源拖入场景，右键选择 Prefab->Unpack 后将根节点拖入Project保存为预制；
    /// 2. 给预制添加Animator组件，新建AnimatorController文件并赋值给该组件；
    /// 3. 给AnimatorController文件加状态机，并给状态机添加clip资源（clip资源位于FBX文件的展开项下，也是来自FBX文件，Unity自己会识别）；
    /// </summary>
    public class ModelMonitor : AssetPostprocessor
    {
        [MenuItem("DouFramework/资源/统一调整所有模型导入格式", false, 10000)]
        public static void ImportAll()
        {
            if (!EditorUtility.DisplayDialog("提示", "统一调整所有模型导入格式？", "确定", "取消"))
            {
                return;
            }

            AssetDatabase.StartAssetEditing();
            try
            {
                var assetPaths = AssetDatabase.GetAllAssetPaths();
                for (int i = 0; i < assetPaths.Length; i++)
                {
                    if (EditorUtility.DisplayCancelableProgressBar("调整模型导入格式", $"{i + 1}/{assetPaths.Length}", (float) i / (float) assetPaths.Length))
                    {
                        break;
                    }
                    Process(assetPaths[i]);
                }
            }
            finally
            {
                EditorUtility.ClearProgressBar();
                AssetDatabase.StopAssetEditing();
                AssetDatabase.Refresh();
            }
        }

        [MenuItem("Assets/DouFramework/资源/调整选中模型导入格式", false, 10000)]
        public static void ImportSelected()
        {
            AssetDatabase.StartAssetEditing();
            try
            {
                for (int i = 0; i < Selection.objects.Length; i++)
                {
                    if (EditorUtility.DisplayCancelableProgressBar("调整模型导入格式", $"{i + 1}/{Selection.objects.Length}", (float) i / (float) Selection.objects.Length))
                    {
                        break;
                    }
                    Process(AssetDatabase.GetAssetPath(Selection.objects[i]));
                }
            }
            finally
            {
                EditorUtility.ClearProgressBar();
                AssetDatabase.StopAssetEditing();
                AssetDatabase.Refresh();
            }
        }
        
        private static void Process(string assetPath)
        {
            var ext = Path.GetExtension(assetPath).ToLower();
            if (ext != ".fbx" && ext != ".obj" && ext != ".3ds")
            {
                return;
            }
            
            var modelImporter = AssetImporter.GetAtPath(assetPath) as ModelImporter;
            if (modelImporter == null)
            {
                return;
            }
            
            var objs = AssetDatabase.LoadAllAssetsAtPath(assetPath);
            for (int i = 0; i < objs.Length; i++)
            {
                var model = objs[i] as GameObject;
                if (model != null)
                {
                    try
                    {
                        ImportRendererSetting(model.GetComponentsInParent<Renderer>());
                        ImportRendererSetting(model.GetComponentsInChildren<Renderer>());
                        ImportAniSetting(AnimationUtility.GetAnimationClips(model));
                    }
                    catch (Exception e)
                    {
                        Log.Error($"模型\"{assetPath}\"导入处理异常：{e}");
                    }
                }
            }
            AssetDatabase.ImportAsset(assetPath, ImportAssetOptions.Default);
        }
        
        void OnPreprocessModel()
        {
            var modelImporter = assetImporter as ModelImporter;
            modelImporter.importLights = false;
            modelImporter.materialImportMode = ModelImporterMaterialImportMode.None;
            modelImporter.meshCompression = ModelImporterMeshCompression.Low;
            var externalObjectMap = modelImporter.GetExternalObjectMap();
            foreach (var remap in externalObjectMap)
            {
                modelImporter.RemoveRemap(remap.Key);
            }
            if (modelImporter.isReadable)
            {
                Log.Warning($"文件\"{assetPath}\"网格可读（Read Write Enabled）开启，会增大内存，请确认！");
            }
        }

        void OnPostprocessModel(GameObject model)
        {
            if (model != null) {
                try
                {
                    ImportRendererSetting(model.GetComponentsInParent<Renderer>());
                    ImportRendererSetting(model.GetComponentsInChildren<Renderer>());
                    ImportAniSetting(AnimationUtility.GetAnimationClips(model));
                }
                catch (Exception e)
                {
                    Log.Error($"模型\"{assetPath}\"导入处理异常：{e}");
                }
            }
        }
        
        private static void ImportRendererSetting(Renderer[] renderers)
        {
            if (renderers != null)
            {
                foreach (var renderer in renderers)
                {
                    renderer.sharedMaterial = null;
                    renderer.sharedMaterials = new Material[renderer.sharedMaterials.Length];
                }
            }
        }
        
        // 浮点数精度压缩到 f3
        private static void ImportAniSetting(AnimationClip[] animationClips)
        {
            foreach (var animationClip in animationClips) {
                var curves = AnimationUtility.GetAllCurves(animationClip);
                for (int i = 0; i < curves.Length; i++)
                {
                    AnimationClipCurveData curveDate = curves[i];
                    if (curveDate.curve == null || curveDate.curve.keys == null)
                    {
                        continue;
                    }
                    var keyFrames = curveDate.curve.keys;
                    for (int j = 0; j < keyFrames.Length; j++)
                    {
                        var key = keyFrames[j];
                        key.value = float.Parse(key.value.ToString("f3"));
                        key.inTangent = float.Parse(key.inTangent.ToString("f3"));
                        key.outTangent = float.Parse(key.outTangent.ToString("f3"));
                        keyFrames[j] = key;
                    }
                    curveDate.curve.keys = keyFrames;
                    animationClip.SetCurve(curveDate.path, curveDate.type, curveDate.propertyName, curveDate.curve);
                }
            }
        }
    }
}
